package com.yanmaohu.nikita.parser;

import com.yanmaohu.nikita.NikitaException;
import com.yanmaohu.nikita.data.TypeModifier;
import com.yanmaohu.nikita.data.function.LocalVariableModifier;
import com.yanmaohu.nikita.data.structure.MemberModifier;
import com.yanmaohu.nikita.scanner.SourceCode;
import com.yanmaohu.nikita.scanner.Token;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Bright Lee
 */
public final class ModifierParser {

    private Parser parser;

    protected ModifierParser(Parser parser) {
        this.parser = parser;
    }

    private SourceCode getCode() {
        return parser.getCode();
    }

    public TypeModifier tryParseTypeModifier() {
        int markedIndex = mark();
        Token token = scan();
        int beginIndex = token.getBeginIndex();
        final boolean native_;
        if (token.isNative()) {
            native_ = true;
            markedIndex = mark();
            token = scan();
        } else {
            native_ = false;
        }
        int endIndex = token.getEndIndex();
        final boolean protected_;
        final boolean public_;
        if (token.isProtected()) {
            protected_ = true;
            public_ = false;
        } else if (token.isPublic()) {
            protected_ = false;
            public_ = true;
        } else {
            protected_ = false;
            public_ = false;
            reset(markedIndex);
        }
        if (native_ == false && protected_ == false && public_ == false) {
            return null;
        }
        return new TypeModifier(beginIndex, endIndex, native_, protected_, public_);
    }

    public MemberModifier tryParseMemberModifier() {
        boolean _mutable;
        int markedIndex = mark();
        Token token = scan();
        final int beginIndex = token.getBeginIndex();
        int endIndex = token.getEndIndex();
        if (token.isMutable()) {
            _mutable = true;
        } else {
            reset(markedIndex);
            _mutable = false;
        }
        List<Boolean> arrayMutableList = null;
        while (true) {
            markedIndex = mark();
            token = scan();
            if (!token.isLeftBrk()) {
                reset(markedIndex);
                break;
            }
            boolean mutable_;
            token = scan();
            if (token.isMutable()) {
                mutable_ = true;
                token = scan();
            } else {
                mutable_ = false;
            }
            if (!token.isRightBrk()) {
                throw new NikitaException(getCode(), token, "This must be a ].");
            }
            endIndex = token.getEndIndex();
            if (arrayMutableList == null) {
                arrayMutableList = new ArrayList<>();
            }
            arrayMutableList.add(mutable_);
        }
        markedIndex = mark();
        token = scan();
        boolean protected_;
        boolean public_;
        if (token.isProtected()) {
            protected_ = true;
            public_ = false;
            endIndex = token.getEndIndex();
        } else if (token.isPublic()) {
            protected_ = false;
            public_ = true;
            endIndex = token.getEndIndex();
        } else {
            protected_ = false;
            public_ = false;
            reset(markedIndex);
        }
        if (_mutable == false && (arrayMutableList == null || arrayMutableList.isEmpty()) && protected_ == false && public_ == false) {
            return null;
        }
        return new MemberModifier(beginIndex, endIndex, _mutable, arrayMutableList, protected_, public_);
    }

    public LocalVariableModifier tryParseLocalVariableModifier() {
        int markedIndex = mark();
        Token token = scan();
        if (token.isMutable()) {
            return new LocalVariableModifier(token.getBeginIndex(), token.getEndIndex(), true);
        }
        reset(markedIndex);
        return null;
    }

    int mark() {
        return parser.mark();
    }

    Token scan() {
        return parser.scan();
    }

    void reset(int markedIndex) {
        parser.reset(markedIndex);
    }

    void reset() {
        parser.reset();
    }

}
